home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 22 / Cream of the Crop 22.iso / program / cgazv4n2.zip / XFIND.C < prev   
Text File  |  1989-11-03  |  32KB  |  662 lines

  1. /********************************* XFIND.C **********************************
  2. *   Author:  Bill Lee.
  3. *   Copyright (c) 1989 Bill Lee.  All rights reserved.  Use freely for any
  4. *       non-military purpose; acknowledgment of copyright and retention of
  5. *       copyright notice is required.
  6. *   Purpose:  Search one or more text files for multiple keywords
  7. *       simultaneously.
  8. *   Version:  1.0 (see VERSION macro), 89aug26.
  9. *   Compilers: Microsoft C v5.10.
  10. *              Turbo C 2.0.
  11. *   Memory model:  Small, but should work with any model.
  12. *   Operating systems:  Tested with IBM DOS v3.21, DOS v4.01, and
  13. *       OS/2 Standard Edition v1.10 (protected mode and DOS mode).
  14. *
  15. *   Compiler switches:  none.
  16. *   Compiling and linking:  cl xfind.c msrch.obj expandwi.obj
  17. *
  18. *   Usage:  XFIND "text string A" ["text string B"...]
  19. *                   {[/Ffilespec [/Ffilespec...] | < stdin} [> stdout]
  20. *                   [/H] [/I] [/O[C][L][N][S][1]] [/S] [/V]
  21. *
  22. ******************************************************************************/
  23.  
  24. #include <conio.h>
  25. #include <ctype.h>
  26. #include <stddef.h>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include <msrch.h>
  31.  
  32. /*  Manifest constants, macros: */
  33. #define COPYRIGHT   "Copyright (c) 1989 Bill Lee.  All Rights Reserved."
  34. #define VERSION     "v1.0"                                /* current version */
  35. #define SWITCHAR    "/"                          /* default switch character */
  36. #define SWITCHAR_ENV_VAR_NAME   "SWITCHAR"      /* env variable (upper case) */
  37. #define INBUFSIZE   8192                                /* input buffer size */
  38. #define TRUE        (BOOL) 1
  39. #define FALSE       (BOOL) 0
  40.  
  41. /*  Derived types, from OS/2 Presentation Manager's Toolkit v1.1.
  42.     Copyright (c) 1987,1989 Microsoft Corporation. */
  43.  
  44. #define                 VOID    void
  45. #define                 SHORT   short
  46. typedef unsigned short  BOOL;
  47. typedef unsigned char   UCHAR;
  48. typedef unsigned int    UINT;
  49. typedef unsigned short  USHORT;
  50. typedef unsigned long   ULONG;
  51.  
  52. /*  Prototypes for internal functions and for those not in header files: */
  53. VOID XfdErrExit1(USHORT);                                        /* internal */
  54. VOID XfdErrExit2(USHORT, char *);                                /* internal */
  55. VOID XfdUsage(USHORT);                                           /* internal */
  56. int MshRetrieveChar(void);                                       /* internal */
  57. void MshFoundWord(char *);                                       /* internal */
  58. int expandwild(unsigned int *, char **[], int, char *[], int);   /* external */
  59.  
  60. /*  Global data: */
  61.  
  62. static char *npszSwitchar;     /* ptr to SWITCHAR environment variable value */
  63.  
  64. static struct KWORD2 {                    /* struct for keyword text strings */
  65.     struct kword ms;                              /* msrch kword linked list */
  66.     BOOL fCntLine1;                  /* flag: keyword counted once this line */
  67.     BOOL fOutLine1;                   /* flag: line output once this keyword */
  68.     /* "hit" counts for this keyword: */
  69.     ULONG cKeywordFile;                                   /* total this file */
  70.     ULONG cKeywordAccum;                                  /* total all files */
  71.     ULONG cKeywordFile1;                              /* lines hit this file */
  72.     ULONG cKeywordAccum1;                             /* lines hit all files */
  73. } (*pakW1st)[];                /* ptr to 1st element of keyword struct array */
  74.  
  75. /*  Flag and counts for hits on lines:  */
  76. static BOOL fHitLine1 = FALSE;                /* flag: current line hit once */
  77. static ULONG cHitLines;                   /* hit count on lines in this file */
  78. static ULONG cHitLinesAccum = 0L;         /* hit count on lines in all files */
  79.  
  80. static USHORT cTxt = 0;                   /* count of text strings in argv[] */
  81.  
  82. /*  Command line switches:  */
  83. static BOOL fShowSwitches = FALSE;               /* /S see switches flag     */
  84. static BOOL fCaseIns  = FALSE;                   /* /I case Insensitive flag */
  85. static BOOL freVerse  = FALSE;                   /* /V reVerse flag          */
  86. static BOOL fOutCount = FALSE;                   /* /OC Output Counts flag   */
  87. static BOOL fOutLine  = FALSE;                   /* /OL Output Lines flag    */
  88. static BOOL fOutNum   = FALSE;                   /* /ON Output line Num flag */
  89. static BOOL fOutStr   = FALSE;                   /* /OS Output String flag   */
  90. static BOOL fOutOnce  = FALSE;             /* /O1 Output each line once flag */
  91.  
  92. /*  Input file stuff:  */
  93. static char **npaszFname = NULL;      /* ptr to array of expanded file names */
  94. static SHORT iszFname = -1;                  /* index into *npaszFname array */
  95. static FILE *hInput;                                    /* input file handle */
  96. static ULONG cLine = 0L;                      /* line count in current input */
  97. static ULONG cLineAccum = 0L;                        /* line count all input */
  98. static USHORT iInBuffEnd;           /* index to last non-\0 char of szInBuff */
  99. static char *npszInBuff = NULL;                     /* ptr into input buffer */
  100. static UCHAR szInBuff[INBUFSIZE];              /* input buffer (ended by \0) */
  101. static BOOL fFnameOut = FALSE;           /* flag indicating file name output */
  102.  
  103. /*---------------------------------------------------------------------------*/
  104.  
  105. int main(USHORT argc, char *argv[])
  106. {
  107.     USHORT ich0, ich1;                                  /* indices into char */
  108.     USHORT ikW0;                           /* index into KWORD2 struct array */
  109.     USHORT cFspec = 0;                                 /* count of filespecs */
  110.     char **npaszFspec = NULL;                   /* ptr to array of filespecs */
  111.     UINT cFname = 0;                         /* count of expanded file names */
  112.     int iszExpWildCode;                          /* expandwild() return code */
  113.     static char *npaszExpWildErr[4] = { "",  /* texts for expandwild() codes */
  114.         "Insufficient memory",
  115.         "Number of names changed while expanding",
  116.         "Length of names changed while expanding"
  117.     };
  118.  
  119.     /*  If switch character environment variable is defined, use it; otherwise
  120.         use default. */
  121.     if ((npszSwitchar = getenv(SWITCHAR_ENV_VAR_NAME)) == NULL)/* undefined? */
  122.         npszSwitchar = SWITCHAR;                              /* set default */
  123.     else                                            /* defined, check it out */
  124.         if (strlen(npszSwitchar) != 1)
  125.             XfdErrExit1(11);
  126.  
  127.     if (argc < 2)                           /* there must be some parameters */
  128.         XfdErrExit1(1);                         /* output error msg and code */
  129.  
  130.     /*  Scan all input arguments for the text strings and filespecs, and
  131.         count the number of each.  If Help switch found, issue help info and
  132.         exit.  */
  133.     for (ich0 = 1; ich0 < argc; ++ich0) {
  134.         if (argv[ich0][0] == '\0')                      /* null text string? */
  135.             XfdErrExit1(2);                     /* output error msg and code */
  136.         if (argv[ich0][0] != *npszSwitchar) {              /* a text string? */
  137.             ++cTxt;                                     /* count text string */
  138.             continue;
  139.         }
  140.         if (argv[ich0][1] == 'f' || argv[ich0][1] == 'F') {   /* a filespec? */
  141.             ++cFspec;                                            /* count it */
  142.             continue;
  143.         }
  144.         if (argv[ich0][1] == 'h' || argv[ich0][1] == 'H') {         /* help? */
  145.             XfdUsage(2);
  146.             return (0);
  147.         }
  148.     }
  149.  
  150.     /*  There must be at least 1 text string. */
  151.     if (cTxt < 1)
  152.         XfdErrExit1(3);               /* output error msg and exit with code */
  153.  
  154.     /*  Obtain and partially initialize structs and counts for text strings. */
  155.     /*  Allocate structs and zero counts, ptrs, and flags: */
  156.     pakW1st = (struct KWORD2 (*)[]) calloc(cTxt, sizeof(struct KWORD2));
  157.     if (pakW1st == NULL)                             /* no memory available? */
  158.         XfdErrExit1(4);                                   /* exit with error */
  159.     /*  Build linked list: */
  160.     for (ikW0 = 0; ikW0 < cTxt-1; ++ikW0)
  161.         (*pakW1st)[ikW0].ms.next = &(*pakW1st)[ikW0+1].ms;   /* link to next */
  162.  
  163.     /*  Obtain zeroed array of ptrs for filespecs if any specified. */
  164.     if (cFspec > 0) {
  165.         npaszFspec = (char **) malloc((cFspec+1) * sizeof(char *));
  166.         if (npaszFspec == NULL)                      /* no memory available? */
  167.             XfdErrExit1(4);                               /* exit with error */
  168.         hInput = NULL;                  /* say there are files, need to open */
  169.         npaszFspec[cFspec] = NULL;                   /* zero terminating ptr */
  170.     }
  171.     else {
  172.         hInput = stdin;                 /* indicate no disk files, use stdin */
  173.         fFnameOut = TRUE;          /* so file name is never output for stdin */
  174.     }
  175.  
  176.     /*  Scan all input arguments again.  Build linked list of text strings,
  177.         array of filespecs, and set option switches. */
  178.     ikW0 = 0;                                           /* beginning of list */
  179.     cFspec = 0;                                     /* reset for array index */
  180.  
  181.     for (ich0 = 1; ich0 < argc; ++ich0) {
  182.  
  183.         if (argv[ich0][0] != *npszSwitchar) {              /* a text string? */
  184.             (*pakW1st)[ikW0++].ms.word = argv[ich0];       /* save ptr to it */
  185.             continue;
  186.         }
  187.  
  188.         switch (argv[ich0][1]) {
  189.  
  190.             case 'F':                                         /* a Filespec? */
  191.             case 'f':
  192.                 if (argv[ich0][2] == '\0')                /* null file name? */
  193.                     XfdErrExit1(12);
  194.                 /*  Save ptr to filespec, increment for next. */
  195.                 npaszFspec[cFspec++] = &argv[ich0][2];
  196.                 break;
  197.  
  198.             case 'I':                        /* the case Insensitive switch? */
  199.             case 'i':
  200.                 if (argv[ich0][2] != '\0')        /* switch string too long? */
  201.                     XfdErrExit2(6, argv[ich0]);
  202.                 fCaseIns = TRUE;
  203.                 break;
  204.  
  205.             case 'S':                                      /* show switches? */
  206.             case 's':
  207.                 if (argv[ich0][2] != '\0')        /* switch string too long? */
  208.                     XfdErrExit2(6, argv[ich0]);
  209.                 fShowSwitches = TRUE;
  210.                 break;
  211.  
  212.             case 'V':                                     /* reVerse search? */
  213.             case 'v':
  214.                 if (argv[ich0][2] != '\0')        /* switch string too long? */
  215.                     XfdErrExit2(6, argv[ich0]);
  216.                 freVerse = TRUE;
  217.                 break;
  218.  
  219.             case 'O':                                    /* an Output switch */
  220.             case 'o':
  221.                 if (argv[ich0][2] == '\0')                /* switch missing? */
  222.                     XfdErrExit2(6, argv[ich0]);
  223.                 for (ich1 = 2; argv[ich0][ich1] != '\0'; ++ich1) {
  224.                     switch (argv[ich0][ich1]) {
  225.                         case 'C':         /* Output Counts rather than lines */
  226.                         case 'c':
  227.                             fOutCount = TRUE;
  228.                             break;
  229.                         case 'L':                            /* Output Lines */
  230.                         case 'l':
  231.                             fOutLine = TRUE;
  232.                             break;
  233.                         case 'N':                     /* Output line Numbers */
  234.                         case 'n':
  235.                             fOutNum = TRUE;
  236.                             break;
  237.                         case 'S':            /* Output text String with line */
  238.                         case 's':
  239.                             fOutStr = TRUE;
  240.                             break;
  241.                         case '1':        /* Output each input line once only */
  242.                             fOutOnce = TRUE;
  243.                             break;
  244.                         default:                    /* invalid Output switch */
  245.                             XfdErrExit2(6, argv[ich0]);
  246.                     }
  247.                 }
  248.                 break;
  249.  
  250.             default:
  251.                 XfdErrExit2(5, argv[ich0]);             /* invalid parameter */
  252.  
  253.         }   /* switch (argv[ich0][1]) */
  254.  
  255.     }   /* for (ich0 = 1; ich0 < argc; ++ich0) */
  256.  
  257.     /*  Set default switch if it and related switch not set. */
  258.     fOutLine = (!fOutCount && !fOutLine) ? TRUE : fOutLine;
  259.  
  260.     /*  Set to FALSE any switches that are ignored when other switches are set.
  261.         This may save testing multiple switches later. */
  262.     fOutStr = freVerse ? FALSE : fOutStr;
  263.     fOutNum = (fOutCount && !fOutLine) ? FALSE : fOutNum;
  264.  
  265.     /*  If strings not being output, turn on output-once switch as part of
  266.         logic to output a line only once regardless of number of hits on it. */
  267.     fOutOnce = fOutStr ? fOutOnce : TRUE;
  268.  
  269.     /*  Expand any wild card filespecs into actual file names. */
  270.     if (npaszFspec != NULL)
  271.         if ((iszExpWildCode =
  272.             expandwild(&cFname, &npaszFname, -1, npaszFspec, 0)) != 0)
  273.             XfdErrExit2(7, npaszExpWildErr[iszExpWildCode]);
  274.  
  275.     /*  Output to stderr the command line parameters for this execution of
  276.         XFIND. */
  277.     if (fShowSwitches) {
  278.         /*  Option switches:  */
  279.         fprintf(stderr, "XFIND " VERSION " switch settings: ");
  280.         if (fCaseIns)
  281.             fprintf(stderr, " %cI", *npszSwitchar);
  282.         fprintf(stderr, " %cS", *npszSwitchar);
  283.         if (freVerse)
  284.             fprintf(stderr, " %cX", *npszSwitchar);
  285.         if (fOutCount || fOutLine || fOutNum || fOutStr || fOutOnce) {
  286.             fprintf(stderr, " %cO", *npszSwitchar);
  287.             if (fOutCount)    fprintf(stderr, "C");
  288.             if (fOutLine)     fprintf(stderr, "L");
  289.             if (fOutNum)      fprintf(stderr, "N");
  290.             if (fOutStr)      fprintf(stderr, "S");
  291.             if (fOutOnce)     fprintf(stderr, "1");
  292.         }
  293.         fprintf(stderr, "\n");
  294.         /*  Target text strings:  */
  295.         fprintf(stderr, "%u target text strings:\n", cTxt);
  296.         for (ikW0 = 0; ikW0 < cTxt; ++ikW0)
  297.             fprintf(stderr, "    \"%s\"\n", (*pakW1st)[ikW0].ms.word);
  298.         /*  Names of input files:  */
  299.         if (hInput == stdin)
  300.             fprintf(stderr, "Input file is stdin.\n");
  301.         else {
  302.             fprintf(stderr, "%u input file names:\n", cFname);
  303.             for (ich0 = 0; npaszFname[ich0] != NULL; ++ich0)
  304.                 fprintf(stderr, "    \"%s\"\n", npaszFname[ich0]);
  305.         }
  306.     }
  307.  
  308.     /*  If case insensitivity specified, convert text strings to lower. */
  309.     if (fCaseIns)
  310.         for (ikW0 = 0; ikW0 < cTxt; ++ikW0)
  311.             strlwr((*pakW1st)[ikW0].ms.word);
  312.  
  313.     /*  Initialize, do the searching, and clean up. */
  314.     msrch_init(&(*pakW1st)[0].ms);                      /* initialize search */
  315.     msrch_go(MshRetrieveChar, MshFoundWord);               /* perform search */
  316.     msrch_end();                                           /* clean up after */
  317.  
  318.     /*  Output accumulator(s) if requested and if more than 1 file. */
  319.     if (fOutCount && cFname > 1) {
  320.         printf("==== %u-File Total ", cFname);                     /* marker */
  321.         if (fOutStr) {
  322.             if (cHitLinesAccum > 0)
  323.                 printf("Counts:\n");                /* title only if strings */
  324.             for (ikW0 = 0; ikW0 < cTxt; ++ikW0) {
  325.                 if ((*pakW1st)[ikW0].cKeywordAccum > 0)
  326.                     printf("\"%s\": %lu hits in %lu lines\n",
  327.                         (*pakW1st)[ikW0].ms.word,
  328.                         (*pakW1st)[ikW0].cKeywordAccum,
  329.                         (*pakW1st)[ikW0].cKeywordAccum1);
  330.             }
  331.         }
  332.         printf("Lines: %lu hits in %lu lines\n", cHitLinesAccum, cLineAccum);
  333.     }
  334.  
  335.     return (0);                                         /* XFIND normal exit */
  336.  
  337. }   /* main() */
  338.  
  339. /*---------------------------------------------------------------------------*/
  340.  
  341. /*  Return next input character or EOF to msrch_go(), opening and reading
  342.     file(s) as needed. */
  343. int MshRetrieveChar(void)
  344. {
  345.     USHORT ikW0;                                 /* index into KWORD2 struct */
  346.     USHORT uchInBuff;                              /* current char in buffer */
  347.  
  348.     /*  Note: fgets() includes the \n at the end of every line (except the line
  349.     at EOF).  The \n has the effect of terminating the search for that line
  350.     because the DOS or OS/2 command line doesn't permit embedded \n.  Thus a
  351.     target string is not found if it is broken between two lines.  */
  352.  
  353.     if (hInput == NULL) {                                  /* file not open? */
  354.         /*  If no more file names, indicate EOF. */
  355.         if (npaszFname == NULL || *(npaszFname+(++iszFname)) == NULL) {
  356.             return (EOF);
  357.         }
  358.         if ((hInput = fopen(*(npaszFname+iszFname), "r")) == NULL)
  359.             XfdErrExit1(8);
  360.         npszInBuff = NULL;                                   /* buffer empty */
  361.     }
  362.  
  363.     /*  If buffer ptr not initialized or input line all used, refill buffer. */
  364.     if (npszInBuff == NULL || *(++npszInBuff) == '\0') {
  365.         if (npszInBuff != NULL) {                      /* end of input line? */
  366.             if (fHitLine1 && !freVerse)
  367.                 ++cHitLines;                                     /* count it */
  368.             /*  No hits; an excluded line?  If yes, count and output it. */
  369.             else if (!fHitLine1 && freVerse) {
  370.                 ++cHitLines;                                     /* count it */
  371.                 if (!fFnameOut) {           /* file name not already output? */
  372.                     printf("========== %s\n",
  373.                         *(npaszFname+iszFname));         /* output file name */
  374.                     fFnameOut = TRUE;       /* remember file name was output */
  375.                 }
  376.                 if (fOutLine) {                              /* output line? */
  377.                     if (fOutNum)
  378.                         printf("[%lu] ", cLine);              /* line number */
  379.                     printf("%s%s", szInBuff,                    /* line text */
  380.                         (szInBuff[iInBuffEnd] != '\n') ? "\n" : "");
  381.                 }
  382.             }
  383.         }
  384.         /*  Get an input line. */
  385.         if (fgets(szInBuff, INBUFSIZE, hInput) == NULL) {   /* EOF or error? */
  386.             /*  If EOF, output counts & recurse to open next file.  */
  387.             if (feof(hInput) != 0) {                                 /* EOF? */
  388.                 if (fOutCount && cHitLines > 0) {          /* output counts? */
  389.                     printf("---------- ");                         /* marker */
  390.                     for (ikW0 = 0; ikW0 < cTxt; ++ikW0) {
  391.                         if (fOutStr) {
  392.                             if (ikW0 == 0)  /* title only if strings output: */
  393.                                 printf("Counts:\n");
  394.                             if ((*pakW1st)[ikW0].cKeywordFile > 0)
  395.                                 printf("\"%s\": %lu hits in %lu lines\n",
  396.                                     (*pakW1st)[ikW0].ms.word,        /* text */
  397.                                     (*pakW1st)[ikW0].cKeywordFile,  /* count */
  398.                                     (*pakW1st)[ikW0].cKeywordFile1);/* count */
  399.                         }
  400.                         (*pakW1st)[ikW0].cKeywordAccum +=      /* accumulate */
  401.                             (*pakW1st)[ikW0].cKeywordFile;
  402.                         (*pakW1st)[ikW0].cKeywordFile = 0L;         /* reset */
  403.                         (*pakW1st)[ikW0].cKeywordAccum1 +=     /* accumulate */
  404.                             (*pakW1st)[ikW0].cKeywordFile1;
  405.                         (*pakW1st)[ikW0].cKeywordFile1 = 0L;        /* reset */
  406.                     }
  407.                     /*  Output line counts for the file. */
  408.                     printf("Lines: %lu hits in %lu-line file\n",
  409.                         cHitLines, cLine);
  410.                     cHitLinesAccum += cHitLines;               /* accumulate */
  411.                     cHitLines = 0L;                                 /* reset */
  412.                 }
  413.                 cLineAccum += cLine;                /* accumulate line count */
  414.                 cLine = 0L;                              /* reset line count */
  415.                 /*  Return to caller, after closing input if necessary.  */
  416.                 if (hInput == stdin)
  417.                     return (EOF);         /* if stdin, no more files to open */
  418.                 fclose(hInput);                       /* not stdin, close it */
  419.                 hInput = NULL;                      /* indicate no file open */
  420.                 fFnameOut = FALSE; /* clear flag indicating file name output */
  421.                 return (MshRetrieveChar());
  422.                                         /* recurse to open next file, if any */
  423.             }
  424.             else                            /* not EOF, must be a read error */
  425.                 XfdErrExit1(9);
  426.         }
  427.         /*  If input line is complete, it will have \n at the end except
  428.             perhaps when we are at EOF. */
  429.         iInBuffEnd = strlen(szInBuff) - 1;             /* index of last char */
  430.         if (szInBuff[iInBuffEnd] != '\n')
  431.             if (feof(hInput) == 0)                               /* not EOF? */
  432.                 XfdErrExit1(10);                   /* input buffer too short */
  433.         /*  Set and reset everything for the new input line. */
  434.         npszInBuff = szInBuff;              /* point back to start of buffer */
  435.         ++cLine;                                                /* next line */
  436.         fHitLine1 = FALSE;                                     /* reset flag */
  437.         for (ikW0 = 0; ikW0 < cTxt; ++ikW0) {
  438.             (*pakW1st)[ikW0].fCntLine1 = FALSE;                /* reset flag */
  439.             (*pakW1st)[ikW0].fOutLine1 = FALSE;                /* reset flag */
  440.         }
  441.     }
  442.  
  443.     /*  Return character to caller as lower case or unchanged. */
  444.     uchInBuff = *npszInBuff & 0x00FF;           /* must not be sign-extended */
  445.     return (fCaseIns ? tolower(uchInBuff) : uchInBuff);
  446.  
  447. }   /*  MshRetrieveChar() */
  448.  
  449. /*---------------------------------------------------------------------------*/
  450.  
  451. /*  Called by msrch_go() for each hit in input. */
  452. void MshFoundWord(char *npszWord)      /* npszWord is ptr to the text found. */
  453. {
  454.     USHORT ikW0, ikW1;                         /* indices into KWORD2 struct */
  455.  
  456.     fHitLine1 = TRUE;                       /* remember the hit on this line */
  457.  
  458.     if (freVerse)                    /* if a reVerse search, ignore this one */
  459.         return;
  460.  
  461.     /*  Find same ptr in list to determine index into struct array.
  462.         for should always break and never fall thru.  */
  463.     for (ikW0 = 0; ikW0 < cTxt; ++ikW0)
  464.         if ((*pakW1st)[ikW0].ms.word == npszWord)                  /* equal? */
  465.             break;
  466.  
  467.     ++(*pakW1st)[ikW0].cKeywordFile;                    /* increment counter */
  468.     /*  Increment line hit count if not already counted for this line. */
  469.     if (!(*pakW1st)[ikW0].fCntLine1) {               /* not already counted? */
  470.         ++(*pakW1st)[ikW0].cKeywordFile1;               /* increment counter */
  471.         (*pakW1st)[ikW0].fCntLine1 = TRUE;               /* remember counted */
  472.     }
  473.  
  474.     if (!fFnameOut) {                       /* file name not already output? */
  475.         printf("========== %s\n", *(npaszFname+iszFname));/* output file name*/
  476.         fFnameOut = TRUE;                   /* remember file name was output */
  477.     }
  478.  
  479.     if (fOutLine)                                 /* are we outputing lines? */
  480.         /*  Output line if not already output. */
  481.         if (!(*pakW1st)[ikW0].fOutLine1) {            /* not already output? */
  482.             if (fOutOnce)                        /* set flag only if we care */
  483.                 (*pakW1st)[ikW0].fOutLine1 = TRUE;        /* remember output */
  484.             if (!fOutStr) {                        /* not outputing strings? */
  485.                 /*  If line already output for another string, return. */
  486.                 for (ikW1 = 0; ikW1 < cTxt; ++ikW1)
  487.                     if (ikW1 != ikW0 && (*pakW1st)[ikW1].fOutLine1)
  488.                         return;
  489.             }
  490.             else                                            /* output string */
  491.                 printf("\"%s\": ", (*pakW1st)[ikW0].ms.word);
  492.             if (fOutNum)                          /* output file line number */
  493.                 printf("[%lu] ", cLine);
  494.             printf("%s%s", szInBuff,                     /* output line text */
  495.                 (szInBuff[iInBuffEnd] != '\n') ? "\n" : "");
  496.         }
  497.  
  498.     return;                                                /* back to caller */
  499.  
  500. }   /*  MshFoundWord() */
  501.  
  502. /*---------------------------------------------------------------------------*/
  503.  
  504. /*  Output error msg on stderr and exit with error code. */
  505. VOID XfdErrExit1(USHORT usErrCode)                /* usErrCode is error code */
  506. {
  507.     switch (usErrCode) {
  508.         case 1:
  509.             fprintf(stderr, "\a");
  510.             XfdUsage(2);
  511.             break;
  512.         case 2:
  513.             fprintf(stderr, "\aXFIND--"
  514.                 "Null text string not allowed.\n");
  515.             break;
  516.         case 3:
  517.             fprintf(stderr, "\aXFIND--"
  518.                 "At least 1 text string must be specified.\n");
  519.             XfdUsage(1);
  520.             break;
  521.         case 4:
  522.             fprintf(stderr, "\aXFIND--"
  523.                 "Insufficient memory.\n");
  524.             break;
  525.         case 8:
  526.             fprintf(stderr, "\aXFIND--"
  527.                 "Error opening input file \"%s\":  %s.\n",
  528.                 *(npaszFname+iszFname), strerror(errno));
  529.             break;
  530.         case 9:
  531.             fprintf(stderr, "\aXFIND--"
  532.                 "Error reading %s at line %lu:  %s.\n",
  533.                 hInput == stdin ? "stdin" : *(npaszFname+iszFname), cLine,
  534.                 ferror(hInput) ?
  535.                     (errno != 0 ? strerror(errno) : "unknown cause") :
  536.                     "unknown cause");
  537.             break;
  538.         case 10:
  539.             fprintf(stderr, "\aXFIND--"
  540.                 "Length of line %lu in %s exceeds input buffer size of "
  541.                 "%u bytes.\n", cLine+1,
  542.                 hInput == stdin ? "stdin" : *(npaszFname+iszFname),
  543.                 INBUFSIZE-1);
  544.             break;
  545.         case 11:
  546.             fprintf(stderr, "\aXFIND--"
  547.                 "Value of environment variable " SWITCHAR_ENV_VAR_NAME
  548.                 " must be 1 character in length.\n");
  549.             break;
  550.         case 12:
  551.             fprintf(stderr, "\aXFIND--"
  552.                 "null file name.\n");
  553.             break;
  554.     }
  555.     exit(usErrCode);                                      /* terminate XFIND */
  556. }   /* XfdErrExit() */
  557.  
  558. /*---------------------------------------------------------------------------*/
  559.  
  560. /*  Output error msg on stderr and exit with error code. */
  561. VOID XfdErrExit2(USHORT usErrCode,                /* usErrCode is error code */
  562.     char *npszArg)                      /* npszArg is text specific to error */
  563. {
  564.     switch (usErrCode) {
  565.         case 5:
  566.             fprintf(stderr, "\aXFIND--"
  567.                 "Invalid parameter:  %s.\n", npszArg);
  568.             XfdUsage(1);
  569.             break;
  570.         case 6:
  571.             fprintf(stderr, "\aXFIND--"
  572.                 "Switch invalid or missing:  %s.\n", npszArg);
  573.             XfdUsage(1);
  574.             break;
  575.         case 7:
  576.             fprintf(stderr, "\aXFIND--"
  577.                 "Error during expansion of wildcard file names:  %s.\n",
  578.                 npszArg);
  579.             break;
  580.     }
  581.     exit(usErrCode);                                      /* terminate XFIND */
  582. }   /* XfdErrExit() */
  583.  
  584. /*---------------------------------------------------------------------------*/
  585.  
  586. /*  Output usage syntax via stderr. */
  587. VOID XfdUsage(USHORT sCode)               /* 1 == short help, 2 == long help */
  588. {
  589.     if (sCode == 2)
  590.         fprintf(stderr, "XFIND " VERSION " " COPYRIGHT "\n\n"
  591.             "XFIND searches one or more files for one or more strings "
  592.                 "simultaneously.\n"
  593.             "Output consists of the file names, \"hit\" lines, "
  594.                 "and/or counts of \"hits\".\n\n");
  595.  
  596.     fprintf(stderr,
  597.         "Usage:  XFIND \"text string A\" [\"text string B\"...]\n"
  598.         "              {%cFfilespec [%cFfilespec...] | < stdin} [> stdout]\n"
  599.         "              [%cH] [%cI] [%cO[C][L][N][S][1]] [%cS] [%cV]\n",
  600.         *npszSwitchar, *npszSwitchar, *npszSwitchar, *npszSwitchar,
  601.         *npszSwitchar, *npszSwitchar, *npszSwitchar);
  602.  
  603.     if (sCode == 2) {
  604.  
  605.         fprintf(stderr,                           /* \xFE is a square bullet */
  606.             "\n\xFE   Press any key for remainder of help.");
  607.         getch();
  608.         fprintf(stderr, "\r");      /* return to start of "Press any key..." */
  609.  
  610.         fprintf(stderr,
  611.             /*  (Next line erases above help prompt, watch length.) */
  612.             "Enclose target text strings in double quotes "
  613.             "if they contain white space.\n");
  614.  
  615.         fprintf(stderr,
  616.             "\n%cFfilespec [%cFfilespec...] | < stdin\n"
  617.             "    Input can be 1 or more file specs (wildcards o.k.) or "
  618.                 "via redirection or\n"
  619.             "    piping of stdin.  Maximum line length is %u bytes.\n",
  620.             *npszSwitchar, *npszSwitchar, INBUFSIZE-1);
  621.  
  622.         fprintf(stderr,
  623.             "\nOutput is to stdout; defaults to screen, can be redirected or "
  624.                 "piped.\n");
  625.  
  626.         fprintf(stderr,
  627.             "\nSwitches (%cO switches can be combined, "
  628.                 "and in any order, e.g., %cOLCN):\n"
  629.             "%cH  This Help info output to stderr.  "
  630.                 "(All other parameters ignored.)\n"
  631.             "%cI  Insensitive to case during searching.\n",
  632.             *npszSwitchar, *npszSwitchar, *npszSwitchar, *npszSwitchar);
  633.         fprintf(stderr,
  634.             "%cOC Output \"hit\" Counts at EOF.  "
  635.                 "(Lines not output unless %cOL specified.)\n"
  636.             "%cOL Output Lines.  (Default if neither "
  637.                 "%cOC nor %cOL specified.)\n"
  638.             "%cON Output relative line Number with each line.\n"
  639.             "%cOS Output text String with line and/or count.  "
  640.                 "(Ignored with %cV.)\n"
  641.             "%cO1 Output hits for a string only once for each line.  "
  642.                 "(Useful only with %cOS).\n",
  643.             *npszSwitchar, *npszSwitchar, *npszSwitchar, *npszSwitchar,
  644.             *npszSwitchar, *npszSwitchar, *npszSwitchar, *npszSwitchar,
  645.             *npszSwitchar, *npszSwitchar);
  646.         fprintf(stderr,
  647.             "%cS  Show switches, strings, and "
  648.                 "expanded input file names on stderr.\n"
  649.             "%cV  A reVerse search, i.e., lines excluding strings "
  650.                 "are output and/or counted.\n",
  651.             *npszSwitchar, *npszSwitchar);
  652.  
  653.         fprintf(stderr,
  654.             "\nDefault switch character is %c; change by setting "
  655.                 "environment variable " SWITCHAR_ENV_VAR_NAME ".",
  656.             *npszSwitchar);
  657.     }
  658.  
  659. }   /* XfdUsage() */
  660.  
  661. /*---------------------------------------------------------------------------*/
  662.